/**
 * ex7. 指针 和 make、new分配内存
 *
 * @author cyy
 * @date 2020/11/4
 */
package main

import "fmt"

type Test struct {
	a int
	b int
}

func main() {
	/*
		指针
			普通的变量对应了自己内存地址，而指针是一种让特殊的变量，对应了另一个变量的内存地址
	*/
	fmt.Println("============================ 指针 ============================")
	a := 1
	p := &a // 使用 &字符 取地址
	// a 值：1，				类型：int，	地址：0xc00000a0d0
	// p 值：0xc00000a0d0，	类型：*int，	地址：0xc000006030，	对应的值：1
	fmt.Printf("a 值：%v，类型：%T，地址：%p\n", a, a, &a)
	fmt.Printf("p 值：%v，类型：%T，地址：%p，对应的值：%v\n", p, p, &p, *p /* *p 代表取值 */)

	fmt.Println("\n============================ * 与 & ============================")
	fmt.Println("\n---------------------------- 普通变量 ----------------------------")
	var t1 Test = Test{a: 0, b: 0}
	// t1 值：{0 0}，类型：main.Test，地址：0xc00000a0c0
	fmt.Printf("t1 值：%v，类型：%T，地址：%p\n", t1, t1, &t1)

	tt1 := t1
	// tt1 值：{0 0}，类型：main.Test，地址：0xc00000a0f0
	fmt.Printf("tt1 值：%v，类型：%T，地址：%p\n", tt1, tt1, &tt1)

	t1.a = 1         // 修改原t1中a的值
	fmt.Println(t1)  // {1 0}
	fmt.Println(tt1) // {0 0}	发现此处tt1的a值并没有改变

	fmt.Println("\n---------------------------- 指针变量 ----------------------------")
	var t2 *Test = &Test{a: 0, b: 0}
	// t2 值：&{0 0}，类型：*main.Test，地址：0xc000006048，指向地址：0xc00000a140
	fmt.Printf("t2 值：%v，类型：%T，地址：%p，指向地址：%p\n", t2, t2, &t2, t2)

	tt2 := t2
	// tt2 值：&{0 0}，类型：*main.Test，地址：0xc000006050，指向地址：0xc00000a140
	fmt.Printf("tt2 值：%v，类型：%T，地址：%p，指向地址：%p\n", tt2, tt2, &tt2, tt2)

	t2.a = 1         // 修改原t2中a的值
	fmt.Println(t2)  // &{1 0}
	fmt.Println(tt2) // &{1 0} 发现此处tt2的a值也发生了改变

	/*
		值类型与引用类型
			go中的 数组、基本数据类型、结构体 是值类型
			go中的 切片、map 是引用类型
	*/
	fmt.Println("\n============================ 值类型与引用类型 ============================")
	fmt.Println("\n---------------------------- 数组 ----------------------------")
	arr1 := [...]int{1, 2, 3}
	// arr1 值：[1 2 3]，类型：[3]int，地址：0xc000012380
	fmt.Printf("arr1 值：%v，类型：%T，地址：%p\n", arr1, arr1, &arr1)
	arr2 := arr1
	// arr1 值：[1 2 3]，类型：[3]int，地址：0xc0000123e0
	fmt.Printf("arr1 值：%v，类型：%T，地址：%p\n", arr2, arr2, &arr2)

	arr1[0] = 4
	fmt.Println("arr1 =", arr1) // [4 2 3]
	fmt.Println("arr2 =", arr2) // [1 2 3]

	fmt.Println("\n---------------------------- 切片 ----------------------------")
	slice01 := []int{1, 2, 3}
	// slice01 值：[1 2 3]，类型：[]int，地址：0xc0000044a0，指向地址：0xc000012480
	fmt.Printf("slice01 值：%v，类型：%T，地址：%p，指向地址：%p\n", slice01, slice01, &slice01, slice01)
	slice02 := slice01
	// slice02 值：[1 2 3]，类型：[]int，地址：0xc000004520，指向地址：0xc000012480
	fmt.Printf("slice02 值：%v，类型：%T，地址：%p，指向地址：%p\n", slice02, slice02, &slice02, slice02)

	slice02[0] = 0
	fmt.Println("slice01 =", slice01) // [0 2 3]
	fmt.Println("slice02 =", slice02) // [0 2 3]

	/*
		内存分配
			对于引用类型变量，在使用时不仅需要声明，还需要为其分配内存空间，
			而值类型的声明不需要，因为他们在声明的时候已经默认分配好了内存空间。
			new：func new(Type) *Type

			make：func make(t Type, size ...IntegerType) Type
				只用于slice、map以及channel的内存才能创建，因为这三者本身就是引用类型，所以返回的就是这三个类型本身而非其指针类型。

	*/
	fmt.Println("\n============================ 内存分配 ============================")
	// new
	t := new(Test)
	tt := t
	// t 值：&{0 0}，类型：*main.Test，地址：0xc000006048，指向的地址：0xc00000a1f0
	fmt.Printf("t 值：%v，类型：%T，地址：%p，指向的地址：%p\n", t, t, &t, t)
	// tt 值：&{0 0}，类型：*main.Test，地址：0xc000006050，指向的地址：0xc00000a1f0
	fmt.Printf("tt 值：%v，类型：%T，地址：%p，指向的地址：%p\n", tt, tt, &tt, tt)

	// make
	m := make(map[int]int)
	fmt.Printf("m 值：%v，类型：%T，地址：%p\n", m, m, &m)

}
